/**
 * Copyright 2008-2009 HAN.BIFANG
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */
package han.bifang.gwt.server.action.service;

import han.bifang.common.exception.BifangErrorCode;
import han.bifang.common.exception.BifangException;
import han.bifang.gwt.client.common.entity.MethodParam;
import han.bifang.gwt.client.common.entity.WebServicesBinding;
import han.bifang.gwt.client.common.entity.WebServicesMethod;
import han.bifang.gwt.client.common.entity.WebServicesMethodWrapper;
import han.bifang.gwt.client.common.entity.WsdlWrapper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpSession;
import javax.wsdl.Binding;
import javax.wsdl.BindingOperation;
import javax.wsdl.Definition;
import javax.wsdl.Message;
import javax.wsdl.Operation;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.PortType;
import javax.wsdl.Service;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.extensions.soap.SOAPBinding;
import javax.wsdl.extensions.soap.SOAPOperation;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;

/**
 * 
 * @Author Kent
 */
public class WsdlUtils {

	public static ArrayList<WebServicesBinding> getAllServices(String url,
			HttpSession session) throws BifangException {
		try {

			ArrayList<WebServicesBinding> ret = new ArrayList<WebServicesBinding>();

			WSDLFactory factory = WSDLFactory.newInstance();
			WSDLReader reader = factory.newWSDLReader();
			// reader.setFeature("javax.wsdl.verbose", true);
			reader.setFeature("javax.wsdl.importDocuments", true);
			Definition def = reader.readWSDL(null, url);
			session.setAttribute("def", def);
			session.setAttribute("url",url);
			
			session.setAttribute("wsdl", getWSDL(url));
			Map m = def.getAllServices();
			Iterator i = m.keySet().iterator();

			while (i.hasNext()) {
				QName q = (QName) i.next();
				WebServicesBinding w = new WebServicesBinding();
				w.setName(q.getLocalPart());
				w.setNamespace(q.getNamespaceURI());
				ret.add(w);
			}
			return ret;

		} catch (Exception e) {
			throw new BifangException(BifangErrorCode.READ_WSDL_ERROR,
					"read wsdl error", e);
		}

	}

	public static WsdlWrapper getWrapper(QName serviceName, HttpSession session) throws BifangException{

		try {
			WsdlWrapper w = new WsdlWrapper();
			Definition def = (Definition) session.getAttribute("def");
			String url = (String)session.getAttribute("url");
			Service s = def.getService(serviceName);
			Port port = getSOAPPort(s.getPorts());

			WebServicesBinding wsb = new WebServicesBinding();
			wsb.setName(serviceName.getLocalPart());
			wsb.setNamespace(serviceName.getNamespaceURI());
			wsb.setEndpointUrl(getEndpoint(port));
			wsb.setWsdlUrl(url);
			
			w.setWebServicesBinding(wsb);

			Binding binding = port.getBinding();

			ArrayList methods = new ArrayList();

			// get operations from binding
			List operations = binding.getBindingOperations();
			PortType portType = binding.getPortType();
			HashMap<String, WebServicesMethodWrapper> tmp = new HashMap<String, WebServicesMethodWrapper>();
			if (operations.size() > 0) {
				
				for (Iterator iter = operations.iterator(); iter.hasNext();) {
					BindingOperation operation = (BindingOperation) iter.next();
					WebServicesMethodWrapper wsmw = new WebServicesMethodWrapper();
					WebServicesMethod wsm = new WebServicesMethod();

					wsm.setMethodName(operation.getName());
					
					

					List exts = operation.getExtensibilityElements();
					for (Iterator iter1 = exts.iterator(); iter1.hasNext();) {
						ExtensibilityElement element = (ExtensibilityElement) iter1
								.next();
						if (element instanceof SOAPOperation) {
							SOAPOperation o = (SOAPOperation) element;
							if(!o.getStyle().equals("document")){
								throw new BifangException(BifangErrorCode.NOT_SUPPORT_ERROR,
										"operation[" + operation.getName() + "] style is '" + o.getStyle() 
										+ "'current release only supports style 'document'");
							}
							wsm.setSoapAction(o.getSoapActionURI());
						}
					}
					tmp.put(operation.getName(), wsmw);
					wsmw.setMethod(wsm);
					methods.add(wsmw);
				}
			}

			// get operations from portType
			if ((portType != null)) {
				operations = binding.getPortType().getOperations();

				for (Iterator iter = operations.iterator(); iter.hasNext();) {
					Operation operation = (Operation) iter.next();
					// BindingOperation operation = (BindingOperation)
					// iter.next();
					WebServicesMethodWrapper wsmw = tmp
							.get(operation.getName());
					MethodParam mp = new MethodParam();
					wsmw.setParams(mp);
					
					Message input = operation.getInput().getMessage();
					Map m = input.getParts();
					for(Iterator i = m.values().iterator(); i.hasNext() ;){
						Part p = (Part)i.next();
						mp.setInputName(p.getElementName().getLocalPart());
						mp.setInputNamespace(p.getElementName().getNamespaceURI());
						break;						
					}
					Message output = operation.getOutput().getMessage();
					m = output.getParts();
					for(Iterator i = m.values().iterator(); i.hasNext() ;){
						Part p = (Part)i.next();
						mp.setOutputName(p.getElementName().getLocalPart());
						mp.setOutputNamespace(p.getElementName().getNamespaceURI());
						break;						
					}					

				}
			}
			w.setMethods(methods);

			return w;
		} catch (Exception e) {
			throw new BifangException(BifangErrorCode.PARSE_WSDL_ERROR,"parse wsdl error",e);
		}

		
	}

	private static Port getSOAPPort(Map portMap) {
		if (portMap == null) {
			return null;
		}

		for (Iterator iter = portMap.values().iterator(); iter.hasNext();) {
			Port port = (Port) iter.next();
			Binding binding = port.getBinding();
			List exts = binding.getExtensibilityElements();
			for (Iterator iter1 = exts.iterator(); iter1.hasNext();) {
				ExtensibilityElement element = (ExtensibilityElement) iter1
						.next();
				if (element instanceof SOAPBinding) {
					return port;
				}
			}
		}

		return null;
	}

	private static String getEndpoint(Port port) {
		if (port == null) {
			return null;
		}

		List exts = port.getExtensibilityElements();
		for (Iterator iter = exts.iterator(); iter.hasNext();) {
			ExtensibilityElement element = (ExtensibilityElement) iter.next();
			if (element instanceof SOAPAddress) {
				SOAPAddress address = (SOAPAddress) element;

				return address.getLocationURI();
			}
		}

		return null;
	}

	public static String getWSDL(String url) throws BifangException {
		GetMethod get = new GetMethod(url);
		try {
			HttpClient httpclient = new HttpClient();

			HttpClientParams params = new HttpClientParams();
			params.setSoTimeout(30 * 1000);
			params.setContentCharset("utf-8");

			httpclient.setParams(params);

			int result = httpclient.executeMethod(get);

			if (String.valueOf(result).startsWith("5")
					|| String.valueOf(result).startsWith("4")) {
				throw new BifangException(BifangErrorCode.READ_WSDL_ERROR,
						"server returns response code " + result
								+ " when getting wsdl file");
			}
			String wsdl = get.getResponseBodyAsString();

			return wsdl;
		} catch (BifangException e) {
			throw e;
		} catch (Exception e) {
			throw new BifangException(BifangErrorCode.READ_WSDL_ERROR,
					"read wsdl error", e);
		}
	}
}
